package com.mgg.auth.filter;

import java.io.IOException;
import java.util.*;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.mgg.auth.constant.SecurityConstants;
import com.mgg.common.entity.ValidateCode;
import com.mgg.auth.enums.AuthResultEnum;
import com.mgg.auth.exception.ValidateCodeCheckException;
import com.mgg.auth.properties.SecurityProperties;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;


/**
 * @author zhailiang
 *
 */
@Component("validateCodeFilter")
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {
	
	private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

	/**
	 * 验证码校验失败处理器
	 */
	@Resource(name = "validateCodeAuthenticationFailureHandler")
	private AuthenticationFailureHandler authenticationFailureHandler;
	
	/**
	 * 系统配置信息
	 */
	@Autowired
	private SecurityProperties securityProperties;
	
	@Autowired
	private ServerProperties serverProperties;

    /**
     * 存放所有需要校验验证码的url
     */
    private List<String> urlList = new ArrayList<>();

	/**
	 * 验证请求url与配置的url是否匹配的工具类
	 */
	private AntPathMatcher pathMatcher = new AntPathMatcher();

	/**
	 * 初始化要拦截的url配置信息
	 */
	@Override
	public void afterPropertiesSet() throws ServletException {
		super.afterPropertiesSet();
        urlList.add(serverProperties.getContextPath() + SecurityConstants.LOGIN_PROCESSING_URL_MOBILE);
        String urlString = securityProperties.getCode().getSms().getUrl();
        if (StringUtils.isNotBlank(urlString)) {
            String[] urls = StringUtils.splitByWholeSeparatorPreserveAllTokens(urlString, ",");
            for (String url : urls) {
                urlList.add(url);
            }
        }
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.web.filter.OncePerRequestFilter#doFilterInternal(
	 * javax.servlet.http.HttpServletRequest,
	 * javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain)
	 */
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws ServletException, IOException {
        logger.info("ValidateCodeFilter -- start");

        for (String url : urlList) {
            if (pathMatcher.match(url, request.getRequestURI())) {
                logger.info("校验请求(" + request.getRequestURI() + ")中的验证码");
                try {
                    ValidateCode validateCode = null;
                    String mobile = request.getParameter("mobile");
                    String key = "code:sms:" + mobile;
                    Object value = redisTemplate.opsForValue().get(key);
                    if (value != null) {
                        validateCode = (ValidateCode) value;
                    }
                    String codeInRequest = request.getParameter("smsCode");

                    if (StringUtils.isBlank(codeInRequest)) {
                        throw new ValidateCodeCheckException(AuthResultEnum.SMSCODE_NONE_ERROR.getCode(),AuthResultEnum.SMSCODE_NONE_ERROR.getMessage());
                    }
                    if (validateCode == null) {
                        throw new ValidateCodeCheckException(AuthResultEnum.SMSCODE_NOT_EXIST_ERROR.getCode(),AuthResultEnum.SMSCODE_NOT_EXIST_ERROR.getMessage());
                    }
                    if (validateCode.isExpried()) {
                        redisTemplate.delete(key);
                        throw new ValidateCodeCheckException(AuthResultEnum.SMSCODE_EXPIRE_ERROR.getCode(),AuthResultEnum.SMSCODE_EXPIRE_ERROR.getMessage());
                    }
                    if (!StringUtils.equals(validateCode.getCode(), codeInRequest)) {
                        throw new ValidateCodeCheckException(AuthResultEnum.SMSCODE_MATCH_ERROR.getCode(),AuthResultEnum.SMSCODE_MATCH_ERROR.getMessage());
                    }
                    logger.info("验证码校验通过");
                } catch (ValidateCodeCheckException exception) {
                    authenticationFailureHandler.onAuthenticationFailure(request, response, exception);
                    return;
                }
            }
        }
		chain.doFilter(request, response);
        logger.info("ValidateCodeFilter -- end");
	}
}
